<!DOCTYPE html>
<html>
<head>
    <title>TensorFlow.js 中文手写识别</title>
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@4.14.0/dist/tf.min.js"></script>
    <style>
        body {
            font-family: "Microsoft YaHei", sans-serif;
            display: flex;
            flex-direction: column;
            align-items: center;
            background-color: #f5f5f5;
        }
        .container {
            background: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
            margin-top: 20px;
        }
        #canvas {
            border: 2px solid #333;
            background: white;
            cursor: crosshair;
        }
        .controls {
            margin-top: 15px;
            display: flex;
            gap: 10px;
        }
        button {
            padding: 8px 16px;
            border: none;
            border-radius: 4px;
            background: #4285f4;
            color: white;
            cursor: pointer;
            font-size: 14px;
        }
        button:hover {
            background: #3367d6;
        }
        #result {
            margin-top: 20px;
            font-size: 20px;
            padding: 10px 20px;
            border-radius: 4px;
            background: #e8f0fe;
        }
        .status {
            margin-top: 10px;
            color: #666;
        }
    </style>
</head>
<body>
    <h1>中文手写识别</h1>
    <div class="container">
        <canvas id="canvas" width="300" height="300"></canvas>
        <div class="controls">
            <button id="clearBtn">清除</button>
            <button id="predictBtn">识别</button>
        </div>
        <div class="status" id="status">加载模型中...</div>
    </div>
    <div id="result">识别结果将显示在这里</div>

    <script>
        // DOM元素
        const canvas = document.getElementById('canvas');
        const ctx = canvas.getContext('2d');
        const clearBtn = document.getElementById('clearBtn');
        const predictBtn = document.getElementById('predictBtn');
        const resultDiv = document.getElementById('result');
        const statusDiv = document.getElementById('status');

        // 模型和字符映射表
        let model;
        let charMap = []; // 存储汉字映射表（索引->汉字）

        // 画布状态
        let isDrawing = false;
        let lastX = 0;
        let lastY = 0;

        // 初始化画布
        function initCanvas() {
            // 白色背景（手写用黑色）
            ctx.fillStyle = 'white';
            ctx.fillRect(0, 0, canvas.width, canvas.height);
            
            // 画笔设置
            ctx.strokeStyle = 'black';
            ctx.lineWidth = 6;
            ctx.lineJoin = 'round';
            ctx.lineCap = 'round';
        }

        // 加载模型和字符映射表
        async function loadModel() {
            try {
                // 加载字符映射表（包含常用3755个汉字）
                const charMapResponse = await fetch('https://raw.githubusercontent.com/chenyuntc/simple-faster-rcnn-pytorch/master/data/chinese_characters.txt');
                charMap = await charMapResponse.text().then(text => text.split('\n').map(c => c.trim()).filter(Boolean));
                
                // 加载预训练的中文识别模型（简化版）
                // 注意：实际使用时可能需要替换为更完整的模型
                model = await tf.loadLayersModel('https://tfjs-models.vercel.app/chinese-ocr/model.json');
                
                statusDiv.textContent = '模型加载完成，请在画布上书写单个汉字，然后点击识别';
            } catch (err) {
                console.error('模型加载失败:', err);
                statusDiv.textContent = '模型加载失败，请检查网络后重试';
            }
        }

        // 绘图相关函数
        function startDrawing(e) {
            isDrawing = true;
            [lastX, lastY] = getCoordinates(e);
        }

        function draw(e) {
            if (!isDrawing) return;
            const [x, y] = getCoordinates(e);
            ctx.beginPath();
            ctx.moveTo(lastX, lastY);
            ctx.lineTo(x, y);
            ctx.stroke();
            [lastX, lastY] = [x, y];
        }

        function stopDrawing() {
            isDrawing = false;
        }

        function getCoordinates(e) {
            const rect = canvas.getBoundingClientRect();
            if (e.type.includes('mouse')) {
                return [e.clientX - rect.left, e.clientY - rect.top];
            } else {
                e.preventDefault(); // 阻止触摸滚动
                return [e.touches[0].clientX - rect.left, e.touches[0].clientY - rect.top];
            }
        }

        // 清除画布
        function clearCanvas() {
            ctx.fillStyle = 'white';
            ctx.fillRect(0, 0, canvas.width, canvas.height);
            resultDiv.textContent = '识别结果将显示在这里';
        }

        // 预测汉字
        async function predictChar() {
            if (!model || charMap.length === 0) {
                resultDiv.textContent = '模型尚未加载完成';
                return;
            }

            try {
                // 预处理图像：缩放为64x64（模型输入尺寸）
                const tempCanvas = document.createElement('canvas');
                const tempCtx = tempCanvas.getContext('2d');
                tempCanvas.width = 64;
                tempCanvas.height = 64;
                
                // 绘制并缩放图像
                tempCtx.fillStyle = 'white';
                tempCtx.fillRect(0, 0, 64, 64);
                tempCtx.drawImage(canvas, 0, 0, 64, 64);
                const imageData = tempCtx.getImageData(0, 0, 64, 64);

                // 转换为张量并预处理
                const tensor = tf.tidy(() => {
                    return tf.browser.fromPixels(imageData)
                        .mean(2) // 转为灰度图
                        .toFloat()
                        .div(255.0) // 归一化到0-1
                        .reshape([1, 64, 64, 1]); // 适配模型输入
                });

                // 预测
                const predictions = await model.predict(tensor);
                const results = await predictions.data();
                const predictedIndex = results.indexOf(Math.max(...results));
                const confidence = Math.max(...results) * 100;

                // 显示结果（映射到汉字）
                const predictedChar = charMap[predictedIndex] || '未知字符';
                resultDiv.textContent = `识别结果: ${predictedChar} (可信度: ${confidence.toFixed(2)}%)`;

                // 清理内存
                tensor.dispose();
                predictions.dispose();
            } catch (err) {
                console.error('识别失败:', err);
                resultDiv.textContent = '识别失败，请重试';
            }
        }

        // 事件监听
        canvas.addEventListener('mousedown', startDrawing);
        canvas.addEventListener('mousemove', draw);
        canvas.addEventListener('mouseup', stopDrawing);
        canvas.addEventListener('mouseout', stopDrawing);

        // 触摸支持
        canvas.addEventListener('touchstart', startDrawing);
        canvas.addEventListener('touchmove', draw);
        canvas.addEventListener('touchend', stopDrawing);

        clearBtn.addEventListener('click', clearCanvas);
        predictBtn.addEventListener('click', predictChar);

        // 初始化
        initCanvas();
        loadModel();
    </script>
</body>
</html>